{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-19T14:00:21.738406400Z",
     "start_time": "2023-11-19T14:00:16.636494800Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\41507\\AppData\\Roaming\\Python\\Python39\\site-packages\\scipy\\__init__.py:146: UserWarning: A NumPy version >=1.16.5 and <1.23.0 is required for this version of SciPy (detected version 1.26.2\n",
      "  warnings.warn(f\"A NumPy version >={np_minversion} and <{np_maxversion}\"\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=9, micro=7, releaselevel='final', serial=0)\n",
      "matplotlib 3.5.0\n",
      "numpy 1.26.2\n",
      "pandas 1.3.4\n",
      "sklearn 1.1.2\n",
      "torch 2.1.0+cpu\n",
      "cpu\n"
     ]
    }
   ],
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 加载数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-19T14:00:22.493598700Z",
     "start_time": "2023-11-19T14:00:21.738406400Z"
    }
   },
   "outputs": [],
   "source": [
    "from torchvision import datasets\n",
    "from torchvision.transforms import ToTensor\n",
    "\n",
    "# fashion_mnist图像分类数据集\n",
    "train_ds = datasets.FashionMNIST(\n",
    "    root=\"data\",\n",
    "    train=True,\n",
    "    download=True,\n",
    "    transform=ToTensor()\n",
    ")\n",
    "\n",
    "test_ds = datasets.FashionMNIST(\n",
    "    root=\"data\",\n",
    "    train=False,\n",
    "    download=True,\n",
    "    transform=ToTensor()\n",
    ")\n",
    "\n",
    "# torchvision 数据集里没有提供训练集和验证集的划分\n",
    "# 当然也可以用 torch.utils.data.Dataset 实现人为划分\n",
    "# 从数据集到dataloader\n",
    "train_loader = torch.utils.data.DataLoader(train_ds, batch_size=16, shuffle=True)\n",
    "val_loader = torch.utils.data.DataLoader(test_ds, batch_size=16, shuffle=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-19T14:00:29.832936500Z",
     "start_time": "2023-11-19T14:00:22.493598700Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(tensor([0.2860]), tensor([0.3205]))\n"
     ]
    }
   ],
   "source": [
    "from torchvision.transforms import Normalize\n",
    "\n",
    "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
    "def cal_mean_std(ds):\n",
    "    mean = 0.\n",
    "    std = 0.\n",
    "    for img, _ in ds:\n",
    "        mean += img.mean(dim=(1, 2))\n",
    "        std += img.std(dim=(1, 2))\n",
    "    mean /= len(ds)\n",
    "    std /= len(ds)\n",
    "    return mean, std\n",
    "\n",
    "\n",
    "# print(cal_mean_std(train_ds))\n",
    "# 0.2860， 0.3205\n",
    "transforms = nn.Sequential(\n",
    "    Normalize([0.2860], [0.3205])\n",
    ")\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型\n",
    "\n",
    "这里我们没有用`nn.Linear`的默认初始化，而是采用了xavier均匀分布去初始化全连接层的权重\n",
    "\n",
    "xavier初始化出自论文 《Understanding the difficulty of training deep feedforward neural networks》，适用于使用`tanh`和`sigmoid`激活函数的方法。当然，我们这里的模型采用的是`relu`激活函数，采用He初始化（何凯明初始化）会更加合适。感兴趣的同学可以自己动手修改并比对效果。\n",
    "\n",
    "|神经网络层数|初始化方式|early stop at epoch| val_loss | vla_acc|\n",
    "|-|-|-|-|-|\n",
    "|20|默认|\n",
    "|20|xaviier_uniform|\n",
    "|20|he_uniform|\n",
    "|...|\n",
    "\n",
    "He初始化出自论文 《Delving deep into rectifiers: Surpassing human-level performance on ImageNet classification》"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-19T14:00:29.918476600Z",
     "start_time": "2023-11-19T14:00:29.832936500Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "               layer_name               \tparamerters num\n",
      "linear_relu_stack.0.weight              \t  78400   \n",
      "linear_relu_stack.0.bias                \t   100    \n",
      "linear_relu_stack.Linear_1.weight       \t  10000   \n",
      "linear_relu_stack.Linear_1.bias         \t   100    \n",
      "linear_relu_stack.Linear_2.weight       \t  10000   \n",
      "linear_relu_stack.Linear_2.bias         \t   100    \n",
      "linear_relu_stack.Linear_3.weight       \t  10000   \n",
      "linear_relu_stack.Linear_3.bias         \t   100    \n",
      "linear_relu_stack.Linear_4.weight       \t  10000   \n",
      "linear_relu_stack.Linear_4.bias         \t   100    \n",
      "linear_relu_stack.Linear_5.weight       \t  10000   \n",
      "linear_relu_stack.Linear_5.bias         \t   100    \n",
      "linear_relu_stack.Linear_6.weight       \t  10000   \n",
      "linear_relu_stack.Linear_6.bias         \t   100    \n",
      "linear_relu_stack.Linear_7.weight       \t  10000   \n",
      "linear_relu_stack.Linear_7.bias         \t   100    \n",
      "linear_relu_stack.Linear_8.weight       \t  10000   \n",
      "linear_relu_stack.Linear_8.bias         \t   100    \n",
      "linear_relu_stack.Linear_9.weight       \t  10000   \n",
      "linear_relu_stack.Linear_9.bias         \t   100    \n",
      "linear_relu_stack.Linear_10.weight      \t  10000   \n",
      "linear_relu_stack.Linear_10.bias        \t   100    \n",
      "linear_relu_stack.Linear_11.weight      \t  10000   \n",
      "linear_relu_stack.Linear_11.bias        \t   100    \n",
      "linear_relu_stack.Linear_12.weight      \t  10000   \n",
      "linear_relu_stack.Linear_12.bias        \t   100    \n",
      "linear_relu_stack.Linear_13.weight      \t  10000   \n",
      "linear_relu_stack.Linear_13.bias        \t   100    \n",
      "linear_relu_stack.Linear_14.weight      \t  10000   \n",
      "linear_relu_stack.Linear_14.bias        \t   100    \n",
      "linear_relu_stack.Linear_15.weight      \t  10000   \n",
      "linear_relu_stack.Linear_15.bias        \t   100    \n",
      "linear_relu_stack.Linear_16.weight      \t  10000   \n",
      "linear_relu_stack.Linear_16.bias        \t   100    \n",
      "linear_relu_stack.Linear_17.weight      \t  10000   \n",
      "linear_relu_stack.Linear_17.bias        \t   100    \n",
      "linear_relu_stack.Linear_18.weight      \t  10000   \n",
      "linear_relu_stack.Linear_18.bias        \t   100    \n",
      "linear_relu_stack.Linear_19.weight      \t  10000   \n",
      "linear_relu_stack.Linear_19.bias        \t   100    \n",
      "linear_relu_stack.Output Layer.weight   \t   1000   \n",
      "linear_relu_stack.Output Layer.bias     \t    10    \n"
     ]
    }
   ],
   "source": [
    "class NeuralNetwork(nn.Module):\n",
    "    def __init__(self, layers_num=2):\n",
    "        super().__init__()\n",
    "        self.transforms = transforms\n",
    "        self.flatten = nn.Flatten()\n",
    "        # 多加几层\n",
    "        self.linear_relu_stack = nn.Sequential(\n",
    "            nn.Linear(28 * 28, 100),  # in_features=784, out_features=300\n",
    "            nn.ReLU(),\n",
    "            nn.AlphaDropout(p=0.2) # 增加dropout，p=0.2表示以0.2的概率将某些神经元置0，防止过拟合\n",
    "        )\n",
    "        # 加19层\n",
    "        for i in range(1, layers_num):\n",
    "            self.linear_relu_stack.add_module(f\"Linear_{i}\", nn.Linear(100, 100))\n",
    "            self.linear_relu_stack.add_module(f\"relu\", nn.ReLU())\n",
    "            nn.AlphaDropout(p=0.2) # 增加dropout\n",
    "        # 输出层\n",
    "        self.linear_relu_stack.add_module(\"Output Layer\", nn.Linear(100, 10))\n",
    "        \n",
    "        # 初始化权重\n",
    "        self.init_weights()\n",
    "        \n",
    "    def init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化全连接层的权重 W\"\"\"\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, nn.Linear):\n",
    "                nn.init.xavier_uniform_(m.weight)\n",
    "                nn.init.zeros_(m.bias)\n",
    "\n",
    "    def forward(self, x):\n",
    "        # x.shape [batch size, 1, 28, 28]\n",
    "        x = self.transforms(x)\n",
    "        x = self.flatten(x)  \n",
    "        # 展平后 x.shape [batch size, 28 * 28]\n",
    "        logits = self.linear_relu_stack(x)\n",
    "        # logits.shape [batch size, 10]\n",
    "        return logits\n",
    "\n",
    "print(f\"{'layer_name':^40}\\tparamerters num\")\n",
    "for idx, (key, value) in enumerate(NeuralNetwork(20).named_parameters()):\n",
    "    print(\"{:<40}\\t{:^10}\".format(key, np.prod(value.shape)))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-19T14:00:29.930717Z",
     "start_time": "2023-11-19T14:00:29.853019400Z"
    }
   },
   "outputs": [],
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        \n",
    "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
    "        pred_list.extend(preds.cpu().numpy().tolist())\n",
    "        label_list.extend(labels.cpu().numpy().tolist())\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list)\n",
    "    return np.mean(loss_list), acc\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-19T14:00:35.276632700Z",
     "start_time": "2023-11-19T14:00:29.930717Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From C:\\Program Files\\Python39\\lib\\site-packages\\keras\\src\\losses.py:2976: The name tf.losses.sparse_softmax_cross_entropy is deprecated. Please use tf.compat.v1.losses.sparse_softmax_cross_entropy instead.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-19T14:00:35.292260100Z",
     "start_time": "2023-11-19T14:00:35.276632700Z"
    }
   },
   "outputs": [],
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-19T14:00:35.314894300Z",
     "start_time": "2023-11-19T14:00:35.292260100Z"
    }
   },
   "outputs": [],
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-19T14:23:58.298675100Z",
     "start_time": "2023-11-19T14:00:35.314894300Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "  0%|          | 0/375000 [00:00<?, ?it/s]",
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "51ee777be4f844e289a24afecc3e3143"
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Early stop at epoch 63 / global_step 236250\n"
     ]
    }
   ],
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits.argmax(axis=-1)\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())    \n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                    \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 100\n",
    "\n",
    "model = NeuralNetwork(layers_num=10)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = nn.CrossEntropyLoss()\n",
    "# 2. 定义优化器 采用SGD\n",
    "# Optimizers specified in the torch.optim package\n",
    "optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "tensorboard_callback = TensorBoardCallback(\"runs/dropout\")\n",
    "tensorboard_callback.draw_model(model, [1, 28, 28])\n",
    "# 2. save best\n",
    "save_ckpt_callback = SaveCheckpointsCallback(\"checkpoints/dropout\", save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=10, min_delta=0.001)\n",
    "\n",
    "model = model.to(device)\n",
    "record = training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=tensorboard_callback,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=len(train_loader)\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-19T14:23:58.757055500Z",
     "start_time": "2023-11-19T14:23:58.314301400Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "<Figure size 864x360 with 2 Axes>",
      "image/png": "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\n"
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "#画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(6 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(record, sample_step=10000)  #横坐标是 steps"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-19T14:24:00.181961600Z",
     "start_time": "2023-11-19T14:23:58.757055500Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.3352\n",
      "accuracy: 0.8852\n"
     ]
    }
   ],
   "source": [
    "# dataload for evaluating\n",
    "#推理时alphadropout的p值是0，通过加dropout，一定程度解决了过拟合\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(\"checkpoints/dropout/best.ckpt\", map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, val_loader, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "pytorch",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.8"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
